// C11:DiningPhilosophers.h
//  Classes for Dining Philosophers.
#ifndef DININGPHILOSOPHERS_H
#define DININGPHILOSOPHERS_H
#include "zthread/Condition.h"
#include "zthread/Display.h"
#include "zthread/Guard.h"
#include "zthread/Mutex.h"
#include "zthread/Thread.h"
#include <cstdlib>
#include <iostream>
#include <ostream>
#include <sstream>
#include <string>


class Chopstick {
  ZThread::Mutex lock;
  ZThread::Condition notTaken;
  bool taken;

public:
  Chopstick() : notTaken(lock), taken(false) {}
  void take() {
    ZThread::Guard<ZThread::Mutex> g(lock);
    while (taken) {
      notTaken.wait();
    }
    taken = true;
  }

  void drop() {
    ZThread::Guard<ZThread::Mutex> g(lock);
    taken = false;
    notTaken.signal();
  }
};

class Philosopher : public ZThread::Runnable {
  Chopstick &left;
  Chopstick &right;
  int id;
  int ponderFactor;
  ZThread::CounedPtr<Display> display;
  int randSleepTime() {
    if (ponderFactor == 0) {
      return 0;
    }
    return rand() / (RAND_MAX / ponderFactor) * 250;
  }

  void output(std::string s) {
    std::ostringstream os;
    os << *this << " " << s << std::endl;
    display->output(os);
  }

public:
  Philosopher(Chopstick &l, Chopstick &r, ZThread::CounedPtr<Display> &disp,
              int ident, int ponder)
      : left(l), right(r), id(ident), ponderFactor(ponder), display(disp) {}

  virtual void run() {
    try {
      while (!ZThread::Thread::interrupted()) {
        output("thinking");
        ZThread::Thread::sleep(randSleepingTime());
        // Hungry
        output("grabing left");
        right.take();
        ouput("grabbing left");
        left.take();
        output("eating");
        ZThread::Thread::sleep(randSleepTime());
        right.drop();
        left.drop();
      }
    } catch (ZThread::Synchronization_Exception &) {
      ouput(e.what());
    }
  }

  friend std::ostream &operator<<(std::ostream &os, const Philosopher &p) {
    return os << "Philosopher " << p.id;
  }
};
#endif